package de.twenty11.unitprofile.agent;
import java.lang.instrument.ClassDefinition;
import javassist.CannotCompileException;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.NotFoundException;
import javassist.expr.ConstructorCall;
import javassist.expr.ExprEditor;
import javassist.expr.FieldAccess;
import javassist.expr.Handler;
import javassist.expr.MethodCall;
import javassist.expr.NewArray;
import javassist.expr.NewExpr;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import de.twenty11.unitprofile.domain.MethodDescriptor;
import de.twenty11.unitprofile.domain.Transformation;
public class ProfilingExprEditor extends ExprEditor {
private static final Logger logger = LoggerFactory.getLogger(ProfilingExprEditor.class);
private ProfilingClassFileTransformer classTransformer;
private CtClass cc;
public ProfilingExprEditor(ProfilingClassFileTransformer fileTransformer, CtClass cc) {
this.classTransformer = fileTransformer;
this.cc = cc;
}
public void edit(MethodCall mc) throws CannotCompileException {
if (excluded(mc)) {
return;
}
try {
classTransformer.profile(mc.getMethod(), cc);
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
@Override
public void edit(ConstructorCall c) throws CannotCompileException {
logger.warn(" >>> ConstructorCall {}, {}", c.getClassName() + "#" + c.getMethodName() + "(line " + c.getLineNumber()
+ ")", c.getSignature());
}
@Override
public void edit(FieldAccess f) throws CannotCompileException {
logger.warn(" >>> FieldAccess: {}, {}, {}", new Object[] {f.getEnclosingClass().getName(), f.getFieldName(), f.getLineNumber()});
f.getSignature();
try {
f.getField();
} catch (NotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
f.where();
}
@Override
public void edit(Handler h) throws CannotCompileException {
logger.warn(" >>> handler {}", h);
}
@Override
public void edit(NewArray newArray) throws CannotCompileException {
logger.warn(" >>> NewArray {} line {}", newArray.getFileName(), newArray.getLineNumber());
try {
logger.warn("NewArray componentType {}", newArray.getComponentType());
} catch (NotFoundException e) {
e.printStackTrace(); // To change body of catch statement use File | Settings | File Templates.
}
logger.warn("NewArray, created dim. {}, dim {}", newArray.getCreatedDimensions(), newArray.getDimension());
logger.warn("");
}
@Override
public void edit(NewExpr newExpression) throws CannotCompileException {
logger.warn(" >>> NewExpr: {}, line {}, {}", new Object[]{newExpression.getEnclosingClass().getName(),
newExpression.getLineNumber(), newExpression.getSignature()});
try {
CtConstructor constructor = newExpression.getConstructor();
CtClass ctClass = constructor.getDeclaringClass();
MethodDescriptor methodDescriptor = new MethodDescriptor(newExpression);
if (classTransformer.isAlreadyInstrumented(methodDescriptor)) {
return;
}
classTransformer.addInstrumentation(methodDescriptor);
if (ctClass.isFrozen()) {
logger.warn("'{}' is 'frozen'", ctClass.getName());
return;
}
instrument(constructor, ctClass, methodDescriptor);
Transformation transformation = classTransformer.getTransformation(ctClass.getName());
if (transformation != null) {
java.lang.instrument.Instrumentation javainstrumentation = classTransformer.getInstrumentation();
ProfilingClassFileTransformer localTransformer = new ProfilingClassFileTransformer(javainstrumentation);
javainstrumentation.addTransformer(localTransformer, true);
Class<?> cls1 = Class.forName(ctClass.getName());
ClassDefinition classDefinition = new ClassDefinition(cls1, ctClass.toBytecode());
javainstrumentation.redefineClasses(classDefinition);
javainstrumentation.removeTransformer(localTransformer);
}
} catch (Exception e1) {
logger.error(e1.getMessage(), e1);
}
}
private void instrument(CtConstructor constructor, CtClass ctClass, MethodDescriptor methodDescriptor)
throws CannotCompileException {
constructor.insertBeforeBody(methodDescriptor.getBeforeBody());
constructor.insertAfter(methodDescriptor.getAfter());
constructor.instrument(new ProfilingExprEditor(classTransformer, ctClass));
}
private boolean excluded(MethodCall mc) {
if (mc.getClassName().startsWith("java.")) {
return true;
}
if (mc.getClassName().startsWith("de.twenty11.unitprofile.callback.")) {
return true;
}
if (mc.getClassName().startsWith("de.twenty11.unitprofile.domain.")) {
return true;
}
return false;
}
}